/*
 * Copyright (c) 2008 VMware, Inc.
 * Copyright (c) 2009 John Pritchard, WTKX Project Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package wtkx.in;

import wtkx.Application;
import wtkx.Scene;
import wtkx.co.ArrayList;
import wtkx.ui.Threads;

import jbxml.Dictionary;
import jbxml.Properties;

import netscape.javascript.JSObject;

import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;

/**
 * Application applet
 *
 * @author gbrown
 * @author jdp
 */
public class Applet
    extends java.applet.Applet 
    implements BackingStore.Container
{


    public class AppletProperties 
        extends Object
        implements Dictionary<String, String> 
    {
        private ArrayList<String> knownKeys = new ArrayList<String>();

        protected AppletProperties(){
            super();
        }

        public String get(Object key) {
            String value = Applet.this.getParameter( (String)key);
            if (null != value)
                this.knownKeys.set( (String)key);
            return value;
        }
        public String put(String key, String value) {
            throw new UnsupportedOperationException();
        }
        public String remove(Object key) {
            throw new UnsupportedOperationException();
        }
        public boolean containsKey(Object key) {
            return (null != Applet.this.getParameter( (String)key));
        }
        public boolean isEmpty() {
            return false;
        }
        public java.util.Iterator<String> iterator(){
            return this.knownKeys.iterator();
        }
    }

    private final static long EVENT_MASK =  (AWTEvent.COMPONENT_EVENT_MASK
                                           | AWTEvent.FOCUS_EVENT_MASK
                                           | AWTEvent.MOUSE_EVENT_MASK
                                           | AWTEvent.MOUSE_MOTION_EVENT_MASK
                                           | AWTEvent.MOUSE_WHEEL_EVENT_MASK
                                           | AWTEvent.KEY_EVENT_MASK);


    protected final Properties properties;

    protected volatile Screen screen;

    protected volatile Scene scene;

    protected volatile Awtk awtk;

    protected volatile BackingStore.Component canvas;


    public Applet() {
        super();
        this.properties = new Properties(new AppletProperties());

        this.enableEvents(EVENT_MASK);
        this.setFocusTraversalKeysEnabled(false);

        this.setLayout(new LM());
        this.canvas = this.ctorNewCanvas();

        this.canvas.setSize(this.getSize());

        this.add( (java.awt.Component)canvas);

        this.setFocusTraversalPolicy(FocusTP.Instance);

        this.setVisible(true);
    }


    protected BackingStore.Component ctorNewCanvas(){
        return new Canvas();
    }
    public final Object eval(String script) {
        try {
            JSObject window = JSObject.getWindow(this);
            return window.eval(script);
        } 
        catch (Throwable throwable) {
            throw new UnsupportedOperationException(throwable);
        }
    }
    public void open(URL url){
        this.getAppletContext().showDocument(url);
    }
    public final Properties getProperties(){
        return this.properties;
    }
    public final Scene getScene(){
        return this.scene;
    }
    public final void init() {
        URL documentBase = this.getDocumentBase();
        if (documentBase != null) {
            String queryString = documentBase.getQuery();
            if (queryString != null) {
                String[] arguments = queryString.split("&");

                for (int cc = 0, ccc = arguments.length; cc < ccc; cc++) {
                    String argument = arguments[cc];
                    String[] property = argument.split("=");

                    if (property.length == 2) {
                        String key, value;
                        try {
                            final String encoding = "UTF-8";
                            key = URLDecoder.decode(property[0], encoding);
                            value = URLDecoder.decode(property[1], encoding);
                            this.properties.put(key, value);
                        }
                        catch(UnsupportedEncodingException exception) {
                        }
                    }
                }
            }
        }

        try {
            URL origin = null;
            URL codeBase = this.getCodeBase();
            if (codeBase != null){
                origin = new URL(codeBase.getProtocol(), codeBase.getHost(),
                                 codeBase.getPort(), "");
            }
            Application application = this.newApplication();

            java.awt.Component component = (java.awt.Component)this.canvas;

            this.scene = new Scene(this.properties, application, this, component, origin);

            this.awtk = new Awtk(this,this.canvas,this.scene);

            this.scene.init(this.awtk);

            this.screen = new Screen(this);

            this.canvas.init(this.awtk,this.screen);
        }
        catch (MalformedURLException exc) {
            exc.printStackTrace();
        }
        catch (ClassNotFoundException exc) {
            exc.printStackTrace();
        }
        catch (InstantiationException exc) {
            exc.printStackTrace();
        }
        catch (IllegalAccessException exc) {
            exc.printStackTrace();
        }
    }
    protected Application newApplication()
        throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        String application = this.getParameter(Application.PARAM);
        if (application == null){
            MetaService service = new MetaService();
            if (service.hasNotValue())
                throw new IllegalStateException("Missing parameter '"+Application.PARAM+"'.");
            else
                application = service.value;
        }

        try {
            Class applicationClass = Class.forName(application);
            return (Application)applicationClass.newInstance();
        }
        catch (ClassNotFoundException exc){
            return this.newAppletScript();
        }
    }
    protected Application newAppletScript(){
        return new AppletScript(this);
    }
    public final void start() {
        BackingStore.Component canvas = this.canvas;
        if (null != canvas){
            canvas.start();
        }
        Scene scene = this.scene;
        if (null != scene)
            scene.startup();
    }
    public final void stop() {
        BackingStore.Component canvas = this.canvas;
        if (null != canvas){
            canvas.stop();
        }
        Scene scene = this.scene;
        if (null != scene)
            scene.shutdown();
    }
    public final void destroy() {

        BackingStore.Component canvas = this.canvas;
        if (null != canvas){
            this.canvas = null;
            canvas.destroy();
        }
        try {
            Awtk awtk = this.awtk;
            if (null != awtk){
                this.awtk = null;
                awtk.destroy();
            }
        }
        finally {
            Scene scene = this.scene;
            if (null != scene){
                this.scene = null;
                scene.destroy();
            }
        }
    }
    @Override
    protected void processComponentEvent(ComponentEvent event) {
        super.processComponentEvent(event);
        Awtk awtk = this.awtk;
        if (null != awtk)
            awtk.processComponentEvent(event);
    }
    @Override
    protected void processFocusEvent(FocusEvent event) {
        super.processFocusEvent(event);
        Awtk awtk = this.awtk;
        if (null != awtk)
            awtk.processFocusEvent(event);
    }
    @Override
    protected void processMouseEvent(MouseEvent event) {
        super.processMouseEvent(event);
        Awtk awtk = this.awtk;
        if (null != awtk)
            awtk.processMouseEvent(event);
    }
    @Override
    protected void processMouseMotionEvent(MouseEvent event) {
        super.processMouseMotionEvent(event);
        Awtk awtk = this.awtk;
        if (null != awtk)
            awtk.processMouseMotionEvent(event);
    }
    @Override
    protected void processMouseWheelEvent(MouseWheelEvent event) {
        super.processMouseWheelEvent(event);
        Awtk awtk = this.awtk;
        if (null != awtk)
            awtk.processMouseWheelEvent(event);
    }
    @Override
    protected void processKeyEvent(KeyEvent event) {
        super.processKeyEvent(event);
        Awtk awtk = this.awtk;
        if (null != awtk)
            awtk.processKeyEvent(event);
    }
}
